/*
 ----------------------------------------------------------------------------------
                           "THE BEER-WARE COPYRIGHT LICENSE"
                                     (Revision 43)
                                      -----------
    Rymek [rymek@rymek.org] wrote this file. As long as You retain this notice and
    this file has been sent directly to You by the author, You can do whatever You
    want with this stuff as long as You don't sell or rent it. If we meet some day,
    and You think this stuff is worth it, You can buy me a beer in return :-)
                                                                 __________
                                                                 Paweł Rymaszewski
  ----------------------------------------------------------------------------------
 */

package ui.windows.forms.searchForm;

import constants.UiConstants;
import exceptions.NoResultsException;
import processing.logic.Search;
import processing.objects.Result;
import processing.objects.Results;
import processing.objects.SearchParameters;
import processing.objects.Server;
import ui.windows.components.models.resultTableModel.ResultTableModel;
import ui.windows.components.panels.messageInformationPanel.MessageInformationPanel;
import ui.windows.components.panels.searchParametersPanel.SearchParamsPanel;
import ui.windows.components.panels.serverInformationPanel.ServerInformationPanel;
import utils.properties.Configuration;

import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import java.sql.SQLException;
import java.util.Enumeration;

/**
 * Search window - it's where search happens
 *
 * @author rymek
 *         User: Rymek
 *         Date: 20.07.11
 *         Time: 20:39
 */
public class SearchForm {

    private JPanel searchWindow;

    private ServerInformationPanel serverInformationPanel;
    private MessageInformationPanel messageInformationPanel;
    private SearchParamsPanel searchParamsPanel;

    private JProgressBar searchProcessProgress;

    private JScrollPane searchResultsPanel;
    private JTable parsedResultTable;
    private ResultTableModel resultTableModel;

    private JButton searchButton;
    private JButton stopButton;

    private final Search search;
    private final Server server;

    private Results parsedResults;
    private int progress = 0;
    private SearchFormSearchThread searchThread;

    private ParsedResultTableListSelectionListener parsedResultTableListSelectionListener;

    private JFrame searchFrame;

    /**
     * Constructor.
     * Also initializes objects.
     *
     * @param search Search object
     * @param server Server Information object
     */
    public SearchForm(Search search, Server server) {

        this.search = search;
        this.server = server;

        this.serverInformationPanel.setInfoMode();
        this.searchProcessProgress.setVisible(false);
        this.searchResultsPanel.setVisible(false);

        SearchFormActionListener searchFormActionListener = new SearchFormActionListener(this);

        this.searchButton.addActionListener(searchFormActionListener);
        this.stopButton.addActionListener(searchFormActionListener);
        this.parsedResultTable.addMouseListener(new SearchFormMouseAdapter(this));
        this.searchProcessProgress.addChangeListener(new SearchFormChangeListener(this));

        this.stopButton.setVisible(false);

        this.messageInformationPanel.setParentFrame(this);
        this.searchParamsPanel.setParentFrame(this);
    }

    /**
     * Returns selected row in result table
     *
     * @return selected row in result table
     */
    public synchronized Result getSelectedResult() {
        return resultTableModel.getSelectedRow(parsedResultTable.convertRowIndexToModel(parsedResultTableListSelectionListener.getCurrentSelectedRow()));
    }

    /**
     * Performs search in separate threads and populates
     * results object with parsed results
     *
     * @param searchParameters SearchParameters object
     */
    public void performSearch(SearchParameters searchParameters) {

        this.prepareNewSearch();

        // resetting parsed results and jtable model
        this.parsedResults = new Results(searchParameters);
        this.resultTableModel.setData(this.parsedResults);

        // calling Garbage Collection to free up some memory
        System.gc();

        // creating search thread and starting it
        this.searchThread = new SearchFormSearchThread(this, this.search, this.server, this.parsedResults, searchParameters);
        this.searchThread.start();

        this.stopButton.requestFocus();
    }

    /**
     * Sets progress's bar size
     *
     * @param length size
     */
    public void setupProgressBarForFetch(int length) {
        this.progress = 0;
        this.searchProcessProgress.setMaximum(length);
        this.searchProcessProgress.setValue(0);
        this.searchProcessProgress.setString("Parsing " + this.progress + " of " + length + " results...");
        this.searchProcessProgress.setStringPainted(true);
        this.searchProcessProgress.setVisible(true);
        this.searchFrame.setPreferredSize(this.searchFrame.getSize());
        this.repackAndResetSizes();
    }

    /**
     * Updates progressbar while fetching messages
     */
    public synchronized void updateProgresBarOnFetch() {
        this.progress++;
        this.searchProcessProgress.setValue(this.progress);
        this.searchProcessProgress.setString("Parsing " + this.progress + " of " + this.searchProcessProgress.getMaximum() + " results...");
    }

    /**
     * Prepares new search
     */
    void prepareNewSearch() {

        this.progress = 0;
        this.searchProcessProgress.setValue(this.progress);
        this.searchProcessProgress.setString("Preparing search...");
        this.searchProcessProgress.setStringPainted(true);
        this.searchProcessProgress.setVisible(true);

        this.showResultTable();
        this.searchButton.setVisible(false);
        this.stopButton.setVisible(true);

        this.messageInformationPanel.lockFieldsForSearch();
        this.searchParamsPanel.lockFieldsForSearch();

        this.repackAndResetSizes();
    }

    /**
     * Sets window into new search mode.
     *
     * @throws NoResultsException thrown when no results were found for given SearchString
     */
    public void finishSearch() throws NoResultsException {

        this.searchThread = null;

        this.searchProcessProgress.setVisible(false);
        this.stopButton.setVisible(false);
        this.searchButton.setVisible(true);

        this.messageInformationPanel.unlockFields();
        this.searchParamsPanel.unlockFields();

        this.repackAndResetSizes();

        if (this.parsedResults.getResultCount() == 0 && this.progress != 0) throw new NoResultsException();
    }

    /**
     * Terminates search process
     *
     * @throws SQLException when database error occurs
     */
    public void terminateSearch() throws SQLException {

        this.searchProcessProgress.setVisible(false);
        this.searchButton.setVisible(true);
        this.stopButton.setVisible(false);
        this.searchProcessProgress.setVisible(false);

        this.messageInformationPanel.unlockFields();
        this.searchParamsPanel.unlockFields();

        this.repackAndResetSizes();
        this.searchFrame.setPreferredSize(this.searchFrame.getSize());

        this.searchThread.terminateSearch();
    }

    /**
     * Sets results visible after clicking
     * search button
     */
    void showResultTable() {
        this.searchResultsPanel.setVisible(true);
        this.searchFrame.setResizable(true);
        this.repackAndResetSizes();
    }

    /**
     * Method resets minimum size of the frame after repack
     */
    void repackAndResetSizes() {
        this.searchFrame.pack();
        this.searchFrame.setMinimumSize(this.searchFrame.getSize());
    }

    /**
     * Custom create some of the UI components in SearchForm
     */
    private void createUIComponents() {

        // custom creating panels
        this.messageInformationPanel = new MessageInformationPanel(this.search);
        this.serverInformationPanel = new ServerInformationPanel(this.server);

        // create Result Table
        this.prepareResultTable();

        // creating search and stop buttons with icon
        this.searchButton = new JButton(Configuration.ICON_SEARCH);
        this.stopButton = new JButton(Configuration.ICON_STOP);
    }

    /**
     * Creates ResultTable with Model and all other
     * needed components
     */
    private void prepareResultTable() {

        // creating result table display
        this.resultTableModel = new ResultTableModel();
        this.parsedResultTable = new JTable(this.resultTableModel);
        this.parsedResultTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        // setting SelectionListener for JTable
        this.parsedResultTableListSelectionListener = new ParsedResultTableListSelectionListener();
        this.parsedResultTable.getSelectionModel().addListSelectionListener(this.parsedResultTableListSelectionListener);

        // setting selection properties
        this.parsedResultTable.setCellSelectionEnabled(false);
        this.parsedResultTable.setRowSelectionAllowed(true);
        this.parsedResultTable.setFocusable(false);

        // setting default table cell renderer to display columns with justified values
        DefaultTableCellRenderer defaultTableCellRenderer = new DefaultTableCellRenderer();
        defaultTableCellRenderer.setHorizontalAlignment(SwingConstants.CENTER);

        for (Enumeration<TableColumn> tableColumns = this.parsedResultTable.getColumnModel().getColumns(); tableColumns.hasMoreElements(); ) {
            tableColumns.nextElement().setCellRenderer(defaultTableCellRenderer);
        }
    }

    /**
     * Creates SearchForm
     */
    public void createSearchWindow() {
        this.searchFrame = new JFrame(UiConstants.WND_SEARCH_NAME + UiConstants.APP_WND_TITLE);
        this.searchFrame.setContentPane(this.searchWindow);
        this.searchFrame.setIconImage(Configuration.ICON_DEFAULT_FRAME);
        this.searchFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        this.searchFrame.setResizable(false);
        this.repackAndResetSizes();
        this.searchFrame.setVisible(true);
    }

    public MessageInformationPanel getMessageInformationPanel() {
        return messageInformationPanel;
    }

    public SearchParamsPanel getSearchParamsPanel() {
        return searchParamsPanel;
    }

    public Results getParsedResults() {
        return parsedResults;
    }

    public JFrame getSearchFrame() {
        return searchFrame;
    }

    public JButton getSearchButton() {
        return searchButton;
    }

    public JButton getStopButton() {
        return stopButton;
    }

    public JProgressBar getSearchProcessProgress() {
        return searchProcessProgress;
    }
}
